package com.seventhdawn.gwt.rpc.scripttag.client;

/**
 * Used to send an HTTP GET request using a {@code script} tag.
 * <p>
 * Expects the resulting data as JSONP in order to fire the appropriate callback
 * function.
 * <p>
 * Users can customize JSONP request timeout by overriding the
 * {@link #getTimeoutMillis() getTimeoutMillis} method.
 * 
 * @see ScriptTagResponseHandler
 * @see ScriptTagServiceProxy
 * @author vojtech.szocs
 * @author Artem Kozarezov
 */
public class ScriptTagRequestInvoker {

    private final String url;
    private final int requestId;

    public ScriptTagRequestInvoker(String url, int requestId) {
        this.url = url;
        this.requestId = requestId;
    }

    /**
     * Sends an HTTP GET request using a {@code script} tag.
     * 
     * @param responseHandler Response handler that gets notified when the
     *            request completes or times out.
     */
    public void send(ScriptTagResponseHandler responseHandler) {
        requestJson(url, requestId, getTimeoutMillis(), responseHandler);
    }

    /**
     * Returns the number of milliseconds to wait for the request to complete.
     * Should the request time out, the
     * {@link ScriptTagResponseHandler#onTimeout() onTimeout} method is called
     * on the response handler.
     * <p>
     * Returns {@code 0} by default (no timeout).
     * 
     * @return Number of milliseconds to wait for the request to complete or
     *         {@code 0} for no timeout.
     */
    protected int getTimeoutMillis() {
        return 0;
    }

    private static native void requestJson(String url, int requestId, int timeoutMillis, ScriptTagResponseHandler responseHandler)
    /*-{
        // Name of the callback function that will get called
        var callback = "__gwt_callback" + requestId;
        
        // Flag used to indicate a completed request
        var callbackDone = callback + "done";
        
        // Prepare the URL with callback parameter
        var callbackUrlParam = @com.seventhdawn.gwt.rpc.scripttag.client.ScriptTagServiceProxy::URL_PARAM_CALLBACK + "=" + callback;
        var urlWithCallback = url + (url.indexOf("?") != -1 ? "&" + callbackUrlParam : "?" + callbackUrlParam);
        
        // Create a script element
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        script.setAttribute("src", urlWithCallback);
        
        // Is timeout active?
        var timeoutActive = timeoutMillis > 0;
        
        // Cleanup function that removes script and callback elements
        var cleanup = function() {
            document.body.removeChild(script);
            delete window[callback];
            delete window[callbackDone];
        };
        
        // Add a callback function to the window object
        // When timeout is active, cleanup will be done upon the timeout event.
        // When timeout isn't active, cleanup will be done just after processing response.
        window[callback] = function(jsonObj) {
            responseHandler.@com.seventhdawn.gwt.rpc.scripttag.client.ScriptTagResponseHandler::onCompletion(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
            window[callbackDone] = true;
            
            if (!timeoutActive) {
                cleanup();
            }
        };
        
        // Add a timeout function if necessary
        if (timeoutActive) {
            setTimeout(function() {
                if (!window[callbackDone]) {
                    responseHandler.@com.seventhdawn.gwt.rpc.scripttag.client.ScriptTagResponseHandler::onTimeout()();
                }
                
                cleanup();
            }, timeoutMillis);
        }
        
        // Attach the script element to the document body
        document.body.appendChild(script);
    }-*/;

}
